Profile picture

[k8s] Ingress 실습 & helm으로 ingress-nginx-controller 설치하기

JaehyoJJAng2023년 04월 06일

▶︎ Ingress

일반적으로 네트워크 트래픽은 **Ingress(인그레스)**와 **Egress(이그레스)**로 나뉘어진다.

Ingress는 외부에서 내부로 들어노는 트래픽을 의미하고

Egress는 내부에서 외부로 나가는 트래픽을 의미한다.

쿠버네티스에서 ingress는 외부로부터의 접근을 관리하는 API 오브젝트이다.

즉, URL을 통해 HTTP(S)접근 경로를 만들어 외부 트래픽이 내부의 어느 파드에 도달해야 하는지에 대해 정의된 규칙 모음이라고 볼 수 있다.
image


‣ 인그레스와 서비스의 차이점

로드밸런서 타입의 서비스는 클라우드 서비스 제공사의 로드밸런서에 할당된 공인 IP와 포트를 통해 내부 통신을 지원한다.

인그레스는 서비스의 기능 외에도 아래와 같은 기능을 추가적으로 제공한다.

  • 하나의 클러스터에 복수의 도메인을 연결하는 가상 호스팅
  • 클러스터에 접근하는 트래픽을 URL 별로 서로 다른 서비스에 로드밸런싱
  • 클러스터 내에서 구동되는 리소스에 접근할 수 있는 외부 URL 제공
  • HTTPS 사용을 위한 SSL 인증서 로직 처리

첫 번째 항목의 가상 호스팅이란 동일한 IP에 대해 다른 도메인 이름으로 요청이 도착했을 때, 어떻게 처리할 것인지 정의하는 것을 의미한다.


또한 네트워크 계층에 따른 차이점이 있다.

서비스는 여러 파드(pod)로 들어오기 위한 고정 IP 주소와 포트를 제공한다.

이는 OSI 7계층에서 IP와 포트를 기반으로 한 TCP/UDP 프로토콜인 L4 계층에 해당된다.

따라서 서비스는 L4 스위치 역할에 해당된다고 볼 수 있다.

인그레스는 URL을 제공하여 IP가 아닌 경로를 통해 통신한다. 이는 HTTP(S)를 기반으로 처리하는 L7 계층에 해당된다고 볼 수 있다.

따라서 경로 기반의 정교한 라우팅이 가능하다.


‣ Ingress Controller

인그레스 컨트롤러는 앞서 말한 기능들을 인그레스 리소스에 따라 HTTP 로드밸런서를 구성하고 실제로 기능을 수행하는 애플리케이션이다.

외부 접속을 위한 IP와 포트를 생성해 노출하고 내부적으로는 라우팅 규칙에 따라 작동한다.

따라서 인그레스가 작동하려면 인그레스 컨트롤러(Ingress Controller)가 반드시 필요하다.


인그레스 컨트롤러에서 정의하는 로드밸런서는 클러스터 내에서 소프트웨어 형식의 로드밸런서로 구현되거나,
외부에서 실행되는 하드웨어 또는 클라우드 형태의 로드밸런서일 수도 있다.


인그레스 컨트롤러에는 클라우드 서비스 제공사가 제공하는 로드밸런서 형태, 네트워크 회사들이 제공하는 컨트롤러, 오픈소스 기반의 프록시 형태 컨트롤러 등이 있지만,

대표적으로 웹 서버로 유명한 nginx를 사용한 Nginx-Ingress-Controller가 많이 사용된다.

nginx 인그레스 컨트롤러는 nginx 소프트웨어 자체를 리버스 프록시와 로드밸런서로 활용하며 파드(pod)에 배포된다.
image
출처: OpenShift에서 엔터프라이즈급 Ingress Controller가 필요한 이유


‣ 사용 예제

인그레스를 정의하는 yaml 형식의 메타파일을 살펴보자.

test-onetest-two라는 목적지로 라우팅 되기 위해서는 아래와 같이 정의한다.

인그레스의 경우 Nginx ingress controller를 사용한다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: virtual-hosting-ingresss
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  #① 인그레스 라우팅 규칙 시작
  rules:
    #② 라우팅 규칙을 적용할 호스트(도메인) 이름
  - host: test1.example.com
    http:
      paths:
      - pathType: Prefix
        #③ 라우팅 규칙을 적용할 경로
        path: "/"
        backend:
          service:
            #④ 라우팅 목적지 서비스 이름
            name: test-one
            #⑤ 라우팅 목적지 서비스 포트
            port:
              number: 80
    #⑥ 규칙을 적용할 두 번째 호스트(도메인) 이름
  - host: test2.example.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: test-two
            port:
              number: 80

항목별 세부적인 내용은 아래와 같다.

  • ①: rules 태그를 시작으로 인그레스에서 사용할 라우팅 규칙을 선언함.
  • ②: 요청한 호스트(도메인) 이름을 검사하여 해당하는 하위 규칙을 적용함.
  • ③: 사용자가 요청한 호스트(도메인)와 요청 경로를 검사
    • 위 파일에서는 인덱스 페이지("host:ip/")인지를 검사함.
  • ④: 여기까지 조건이 맞는다면 이 요청은 test-one 서비스로 연결된다.
  • ⑤: test-one 서비스의 80 포트로 연결
  • ⑥: 인그레스에서 작동할 두 번째 규칙 역시 호스트(도메인) 조건으로 정의하였음.

두 규칙 모두 요청한 호스트 이름과 일치하는 하위 규칙을 적용하여 라우팅하겠다는 의미이다.

즉, 동일한 쿠버네티스 클러스터에서 각각 다른 도메인 이름 (test1.example.com, test2.example.com)으로 서비스를 구분하는 가상 호스팅의 예시이다.

위 흐름은 아래와 같이 표현할 수 있다.
image


이번에는 동일한 호스트이지만 경로에 따라 다른 서비스를 연결하는 인그레스를 작성해보자.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: fanout-ingress
spec:
  ingressClassName: nginx
  #① 인그레스 라우팅 규칙 시작
  rules:
    #② 라우팅 규칙을 적용할 호스트(도메인) 이름
  - host: test1.example.com
    http:
      paths:
        #③ 라우팅 규칙을 적용할 경로
      - path: /in
        pathType: Prefix
        backend:
          service:
            #④ 라우팅 목적지 서비스 이름
            name: in-svc
            #⑤ 라우팅 목적지 서비스 포트
            port:
              number: 3000
        #⑥ 라우팅 규칙을 적용할 두 번째 경로
      - path: /out
        pathType: Prefix
        backend:
          service:
            name: out-svc
            port:
              number: 3003

동일한 host로 들어온 요청이 두 개의 경로(/in, /out)에 의해 라우팅 되는 규칙이 생성될 것이다.

각각의 요청은 3000, 3003 포트의 서비스로 연결되고 80번 포트를 통해 3개의 파드 중 하나에 접속된다.
image


▶︎ Ingress 실습

  • helm을 설치하고 helm으로 nginx-ingress-controller를 설치한 후, nginx 배포해보는 간단한 실습을 진행해보자.

‣ Helm 설치

Helm Chart는 쿠버네티스 리소스를 생성하기 위해 필요한 파일을 모아놓은 디렉토리라고 할 수 있다.

헬름 템플릿인 values.yaml 파일을 활용하면 설치에 필요한 여러 변수를 한 번에 설정할 수 있고 쿠버네티스 리소스를 최적화하기에도 쉽다.

그리고 헬름 레포지토리는 다양한 헬름 차트를 저장 및 공유하는 저장소이다.

도커 레포지토리와 비슷한 개념이라고 보면된다.


헬름을 설치해보자. 헬름은 쿠버네티스를 구성하는 모든 노드에 설치할 필요없이 마스터 노드에만 설치하면 된다.

# helm 설치 스크립트 다운로드
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3

# 실행 권한 부여
chmod u+x get_helm.sh

# 스크립트 실행
bash get_helm.sh

위 과정은 헬름을 설치하는 과정이다.


헬름을 설치했으니 헬름 레포지토리를 추가해보자. 레포지토리에는 여러 종류가 있다.

helm repo add bitnami https;//charts.bitnami.com/bitnami

레포지토리를 업데이트하자.

helm repo update

현재 활성화된 레포지토리 목록을 출력해보자.

helm repo list

# output
NAME   	URL
bitnami	https://charts.bitnami.com/bitnami

‣ Nginx Ingress Controller 설치

  • 헬름을 이용해 nginx ingress controller를 설치해보자.

nginx ingress controller 레포지토리를 검색해보자.

helm search repo nginx

# output
NAME                            	CHART VERSION	APP VERSION	DESCRIPTION
bitnami/nginx                   	16.0.7       	1.25.5     	NGINX Open Source is a web server that can be a...
bitnami/nginx-ingress-controller	11.1.2       	1.10.1     	NGINX Ingress Controller is an Ingress controll... # 설치할 항목
bitnami/nginx-intel             	2.1.15       	0.4.9      	DEPRECATED NGINX Open Source for Intel is a lig...

nginx-ingress-controller를 pull로 압축된 파일 형태로 다운로드하고 압축을 풀어보자.

# bitnami/nginx-ingress-controller 다운로드
helm pull bitnami/nginx-ingress-controller

# 압축 해제
tar xvfz nginx-ingress-controller-11.1.2.tgz

nginx-ingress-controller는 새로운 네임스페이스에 설치해주도록 할거다. 각 애플리케이션의 용도에 맞춰 전용 네임스페이스를 관리하는 것을 권장한다.

kubectl create namespace custom-nginx

아까 압축을 풀고 생성된 nginx-ingress-controller 디렉토리로 이동하고 nginx-ingress를 설치해보도록 하자.

cd nginx-ingress.controller-11.1.2
helm install --namespace custom-nginx --generate-name bitnami/nginx-ingress-controller -f values.yaml
  • --namespace: nginx-ingress-controller가 설치될 네임스페이스 정의
  • --generate-name: 적절한 이름을 랜덤하게 생성

nginx-ingress-controller가 정상적으로 설치되었는지 확인하자

helm ls -n custom-nginx
# output
NAME                               	NAMESPACE   	REVISION	UPDATED                                	STATUS  	CHART                          	APP VERSION
nginx-ingress-controller-1715592941	custom-nginx	1       	2024-05-13 09:35:43.752671198 +0000 UTC	deployed	nginx-ingress-controller-11.1.2	1.10.1

‣ metalLB 설치

앞의 과정은 쿠버네티스 내부에서 생성한 인그레스를 외부로 노출시키는 과정에서 IP 주소를 할당해 노출시키는 경우이다.

이번에는 온프레미스 상황에서 자체 LoadBalancer를 생성해 자동으로 nginx-ingress-controller에 IP를 부여할 수 있도록 만들어볼거다.


먼저 kube-proxystrictARP를 확인해보자. 쿠버네티스 v1.14.2 버전 이후부터는 strictARP 모드를 사용해야 한다.

kubectl get cm kube-proxy -n kube-system -o yaml | grep 'strictARP'
# output
    strictARP: false # 이 값이 false로 나올텐데 true로 변경해줘야 한다.

# true로 변경
kubectl get cm kube-proxy -n kube-system -o yaml | sed -e "s/strictARP: false/strictARP: true/" | kubectl apply -f - -n kube-system

# 다시 확인
kubectl get cm kube-proxy -n kube-system -o yaml | grep 'strictARP'
# output
    strictARP: true

metallb를 다운로드할 디렉토리를 생성하고 해당 디렉토리로 이동하자.

mkdir metallb; cd metallb

그리고 metallb 레포지토리를 추가하고 metallb 헬름 차트를 검색해보자.

# metallb 레포지토리 추가
helm repo add metallb https://metallb.github.io/metallb

# 헬름 레포지토리 업데이트
helm repo update

# metallb 헬름 차트 검색
helm search repo metallb
# output
NAME           	CHART VERSION	APP VERSION	DESCRIPTION
bitnami/metallb	6.1.4        	0.14.5     	MetalLB is a load-balancer implementation for b...
metallb/metallb	0.14.5       	v0.14.5    	A network load-balancer implementation for Kube... # 설치할 차트

metallb 헬름 차트를 다운로드하자.

# 차트 다운로드
helm pull metallb/metallb

# 압축 해제
tar xvfz metallb-0.14.5.tgz

# 압축 해제된 폴더로 이동
cd metallb-0.14.5

metallb용 네임스페이스 생성

kubectl create namespace custom-metallb

헬름으로 metallb 설치

helm install --namespace custom-metallb --generate-name metallb/metallb -f values.yaml

설치 후 메시지를 보면 CRs를 통해 설정을 할 수 있다고 한다. 따라서 metallb가 관리할 IP 주소 범위를 이따가 설정해주겠다.


우선 metallb를 통해 설치한 오브젝트가 원할하게 작동하고 있는지 확인하자.

kubectl get all -n custom-metallb

# output
NAME                                                READY   STATUS    RESTARTS   AGE
pod/metallb-1715593294-controller-94f7d69fd-kq6lh   1/1     Running   0          3h6m
pod/metallb-1715593294-speaker-5gl9w                4/4     Running   0          3h6m
pod/metallb-1715593294-speaker-65t7b                4/4     Running   0          3h6m
pod/metallb-1715593294-speaker-fp246                4/4     Running   0          3h6m

NAME                              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/metallb-webhook-service   ClusterIP   10.98.108.87   <none>        443/TCP   3h6m

NAME                                        DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
daemonset.apps/metallb-1715593294-speaker   3         3         3       3            3           kubernetes.io/os=linux   3h6m

NAME                                            READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/metallb-1715593294-controller   1/1     1            1           3h6m

NAME                                                      DESIRED   CURRENT   READY   AGE
replicaset.apps/metallb-1715593294-controller-94f7d69fd   1         1         1       3h6m

여기서 metallb-webhook-service의 EXTERNAL-IP가 인 것은 정상이다.

해당 서비스는 클러스터 내부에서만 사용되기 때문이다.


metallb 설정을 변경해주기 위해서 metallb를 설치했던 디렉토리에서 config 파일을 추가해주자.

vim my-config.yaml
# my-config.yaml
---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: my-metallb-config
  namespace: custom-metallb
spec:
  addresses:
    - 192.168.219.40-192.168.219.50
  autoAssign: true
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: my-metallb-config
  namespace: custom-metallb
spec:
  ipAddressPools:
    - my-metallb-config

위처럼 metallb가 사용할 IP 주소 범위를 설정하였다.

  • spec.addresses.[192.168.219.40 ... 192.168.219.50]
    • metallb가 사용할 IP 주소 범위 지정
    • 이때 쿠버네티스 클러스터 IP 주소인 192.168.219.110, 192.168.219.132, 192.168.219.142은 포함하지 않도록 한다.

그 다음으로는 앞서 생성한 my-config.yaml을 실행한다.

kubectl apply -f my-config.yaml

정상적으로 실행됐는지 확인하자.

kubectl describe ipaddresspool.metallb.io my-metallb-config -n custom-metallb

# output
Name:         my-metallb-config
Namespace:    custom-metallb
Labels:       <none>
Annotations:  <none>
API Version:  metallb.io/v1beta1
Kind:         IPAddressPool
Metadata:
  Creation Timestamp:  2024-05-13T09:50:14Z
  Generation:          1
  Resource Version:    191329
  UID:                 8788f578-c1e8-4984-9bd1-06da65c53799
Spec:
  Addresses:
    192.168.219.40-192.168.219.50
  Auto Assign:       true
  Avoid Buggy I Ps:  false
Events:              <none>

아래 명령어를 실행해 nginx-ingress-controller에 EXTERNAL-IP가 할당되었는지 확인해보자.

kubectl get svc -n custom-nginx

# output
NAME                                                  TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)                      AGE
nginx-ingress-controller-1715592941                   LoadBalancer   10.96.31.137   192.168.219.40   80:30320/TCP,443:30629/TCP   3h22m
nginx-ingress-controller-1715592941-default-backend   ClusterIP      10.97.7.29     <none>           80/TCP                       3h22m

정상적으로 IP가 할당되었다.


‣ 인그레스로 하나의 서비스 배포

  • 이제 모든 설정을 마쳤으니 인그레스를 활용해 하나의 서비스를 배포하는 실습을 해보자.

인그레스 실습을 위해 디렉토리를 하나 생성하자.

mkdir ~/k8s/ingress-practice; cd ~/k8s/ingress-practice

디플로이먼트를 생성하자.

# ingress-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ingress-deploy-web
spec:
  replicas: 3
  selector:
    matchLabels:
      name: web-deploy
  template:
    metadata:
      labels:
        name: web-deploy
    spec:
      containers:
        - name: nginx
          image: nginx:latest

서비스를 생성하자.

# ingress-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: ingress-svc
spec:
  selector:
    name: web-deploy
  type: ClusterIP
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

ingress를 생성하자.

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
    - http:
        paths:
          - path: /test01
            pathType: Prefix
            backend:
              service:
                name: ingress-svc
                port:
                  number: 80
  • ingressClassName: kubectl get ingressclass 입력 시 나오는 결과인 Nginx와 동일하게 입력
  • 만약, spec.rules.http.paths.path에서 URL 경로의 하위 경로까지 인삭하고 싶다면 아래와 같이 두 코드를 변경해주면 된다.
    • nginx.ingress.kubernetes.io/rewrite-target: /$2
    • - path: /test01(/|$)(.*)

모두 배포해주자.

kubectl apply -f .

# output
deployment.apps/ingress-deploy-web created
service/ingress-svc created
ingress.networking.k8s.io/ingress created

EXTERNAL-IP192.168.219.40/test01으로 접근해 예상하고 있는 결과가 그대로 나오는지 확인해보자.
image
정상적으로 요청이 성공하였음을 볼 수 있다.


Loading script...